package org.realogic.cfm.util;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Calendar;
import java.util.UUID;

public final class BasicTypeConverter implements Serializable {

	public static final int UTIL_CALENDAR = -999998986;

	public static final int UTIL_DATE = -999998988;

	public static final int MATH_BIGINTEGER = -999998987;

	public static final int ENUM = -999998989;

	private static final long serialVersionUID = 7691463236204070311L;

	private static final DateParser dateParser = new DateParser();

	private BasicTypeConverter() {
	}

	public static Object convert(Object value, int toDataType) {

		try {
			switch (toDataType) {
			case UTIL_DATE: {
				return toUtilDate(value);
			}
			case UTIL_CALENDAR: {
				return toCalendar(value);
			}
			case MATH_BIGINTEGER: {
				return toMathBigInteger(value);
			}
			case Types.BIGINT: {
				return toLong(value);
			}
			case Types.INTEGER: {
				return toInteger(value);
			}
			case Types.TINYINT: {
				return toByte(value);
			}
			case Types.SMALLINT: {
				return toShort(value);
			}
			case Types.NUMERIC: {
				return toBigDecimal(value);
			}
			case Types.DECIMAL: {
				return toBigDecimal(value);
			}
			case Types.REAL: {
				return toFloat(value);
			}
			case Types.DOUBLE: {
				return toDouble(value);
			}
			case Types.FLOAT: {
				return toDouble(value);
			}
			case Types.BOOLEAN: {
				return toBoolean(value);
			}
			case Types.TIMESTAMP: {
				return toTimestamp(value);
			}
			case Types.DATE: {
				return toDate(value);
			}
			case Types.VARCHAR: {
				return toString(value);
			}
			case Types.CHAR: {
				return toString(value);
			}
			case Types.OTHER: {
				return value;
			}
			case Types.JAVA_OBJECT: {
				return value;
			}
			case Types.BINARY:
			case Types.LONGVARBINARY:
			case Types.BLOB: {
				return value;
			}
			case Types.LONGVARCHAR:
			case Types.CLOB: {
				return value;
			}
			default: {
				String msg = "Unhandled data type [" + toDataType + "] converting [" + value + "]";
				throw new RuntimeException(msg);
			}
			}
		} catch (ClassCastException e) {
			String m = "ClassCastException converting to data type [" + toDataType + "] value [" + value + "]";
			throw new RuntimeException(m);
		}
	}

	public static String toString(Object value) {

		if (value == null) {
			return null;
		}
		if (value instanceof String) {
			return (String) value;
		}
		if (value instanceof char[]) {
			return String.valueOf((char[]) value);
		}

		return value.toString();
	}

	public static Boolean toBoolean(Object value) {
		Boolean tmp = null;
		if (value != null) {
			if (value instanceof Boolean) {
				tmp = (Boolean) value;
			} else {
				tmp = Boolean.valueOf(value.toString());
			}
		}
		return tmp;
	}

	public static UUID toUUID(Object value) {

		if (value == null) {
			return null;
		}
		if (value instanceof String) {
			return UUID.fromString((String) value);
		}
		return (UUID) value;
	}

	public static BigDecimal toBigDecimal(Object value) {

		if (value == null) {
			return null;
		}
		if (value instanceof BigDecimal) {
			return (BigDecimal) value;
		}
		return new BigDecimal(value.toString());
	}

	public static Float toFloat(Object value) {

		if (value == null) {
			return null;
		}
		if (value instanceof Float) {
			return (Float) value;
		}
		if (value instanceof Number) {
			return Float.valueOf(((Number) value).floatValue());
		}
		return Float.valueOf(value.toString());
	}

	public static Short toShort(Object value) {

		if (value == null) {
			return null;
		}
		if (value instanceof Short) {
			return (Short) value;
		}
		if (value instanceof Number) {
			return Short.valueOf(((Number) value).shortValue());
		}
		return Short.valueOf(value.toString());
	}

	public static Byte toByte(Object value) {

		if (value == null) {
			return null;
		}
		if (value instanceof Byte) {
			return (Byte) value;
		}
		return Byte.valueOf(value.toString());
	}

	public static Integer toInteger(Object value) {

		if (value == null) {
			return null;
		}
		if (value instanceof Integer) {
			return (Integer) value;
		}
		if (value instanceof Number) {
			return Integer.valueOf(((Number) value).intValue());
		}
		return Integer.valueOf(value.toString());
	}

	public static Long toLong(Object value) {

		if (value == null) {
			return null;
		}
		if (value instanceof Long) {
			return (Long) value;
		}
		if (value instanceof String) {
			return Long.valueOf((String) value);
		}
		if (value instanceof Number) {
			return Long.valueOf(((Number) value).longValue());
		}
		if (value instanceof java.util.Date) {
			return Long.valueOf(((java.util.Date) value).getTime());
		}
		if (value instanceof Calendar) {
			return Long.valueOf(((Calendar) value).getTime().getTime());
		}
		return Long.valueOf(value.toString());
	}

	public static BigInteger toMathBigInteger(Object value) {

		if (value == null) {
			return null;
		}
		if (value instanceof BigInteger) {
			return (BigInteger) value;
		}
		return new BigInteger(value.toString());
	}

	public static Double toDouble(Object value) {

		if (value == null) {
			return null;
		}
		if (value instanceof Double) {
			return (Double) value;
		}
		if (value instanceof Number) {
			return Double.valueOf(((Number) value).doubleValue());
		}
		return Double.valueOf(value.toString());
	}

	public static Timestamp toTimestamp(Object value) {

		if (value == null) {
			return null;
		}
		if (value instanceof Timestamp) {
			return (Timestamp) value;

		} else if (value instanceof java.util.Date) {
			// no nanos here... so hopefully ok
			return new Timestamp(((java.util.Date) value).getTime());

		} else if (value instanceof Calendar) {
			return new Timestamp(((Calendar) value).getTime().getTime());

		} else if (value instanceof String) {
			return dateParser.parseTimestamp((String) value);

		} else if (value instanceof Number) {
			return new Timestamp(((Number) value).longValue());

		} else {
			String msg = "Unable to convert [" + value.getClass().getName() + "] into a Timestamp.";
			throw new RuntimeException(msg);
		}
	}

	public static java.sql.Time toTime(Object value) {

		if (value == null) {
			return null;
		}
		if (value instanceof java.sql.Time) {
			return (java.sql.Time) value;

		} else if (value instanceof String) {
			return java.sql.Time.valueOf((String) value);

		} else {
			String m = "Unable to convert [" + value.getClass().getName() + "] into a java.sql.Date.";
			throw new RuntimeException(m);
		}
	}

	public static java.sql.Date toDate(Object value) {

		if (value == null) {
			return null;
		}
		if (value instanceof java.sql.Date) {
			return (java.sql.Date) value;

		} else if (value instanceof java.util.Date) {
			return new java.sql.Date(((java.util.Date) value).getTime());

		} else if (value instanceof Calendar) {
			return new java.sql.Date(((Calendar) value).getTime().getTime());

		} else if (value instanceof String) {
			return dateParser.parseDate((String) value);

		} else if (value instanceof Number) {
			return new java.sql.Date(((Number) value).longValue());

		} else {
			String m = "Unable to convert [" + value.getClass().getName() + "] into a java.sql.Date.";
			throw new RuntimeException(m);
		}
	}

	public static java.util.Date toUtilDate(Object value) {

		if (value == null) {
			return null;
		}
		if (value instanceof java.sql.Timestamp) {
			// loss of nanos precision
			return new java.util.Date(((java.sql.Timestamp) value).getTime());
		}
		// DEVNOTE: strictly speaking do I need to convert a java.sql.Date to
		// java.util.Date? equals() is symmetrical so perhaps this is not
		// really required?
		if (value instanceof java.sql.Date) {
			return new java.util.Date(((java.sql.Date) value).getTime());
		}
		if (value instanceof java.util.Date) {
			return (java.util.Date) value;

		} else if (value instanceof Calendar) {
			return ((Calendar) value).getTime();

		} else if (value instanceof String) {
			return dateParser.parseDate((String) value);

		} else if (value instanceof Number) {
			return new java.util.Date(((Number) value).longValue());

		} else {
			throw new RuntimeException("Unable to convert [" + value.getClass().getName() + "] into a java.util.Date");
		}
	}

	public static Calendar toCalendar(Object value) {

		if (value == null) {
			return null;
		}
		if (value instanceof Calendar) {
			return (Calendar) value;

		} else if (value instanceof java.util.Date) {
			java.util.Date date = ((java.util.Date) value);
			return toCalendarFromDate(date);

		} else if (value instanceof String) {
			java.util.Date date = dateParser.parse((String) value);
			return toCalendarFromDate(date);

		} else if (value instanceof Number) {
			long timeMillis = ((Number) value).longValue();
			java.util.Date date = new java.util.Date(timeMillis);
			return toCalendarFromDate(date);

		} else {
			String m = "Unable to convert [" + value.getClass().getName() + "] into a java.util.Date";
			throw new RuntimeException(m);
		}
	}

	private static Calendar toCalendarFromDate(java.util.Date date) {

		Calendar cal = Calendar.getInstance();
		cal.setTime(date);

		return cal;
	}

}
